package com.camel.chart.func;

import cn.hutool.core.util.ObjectUtil;
import com.camel.chart.enums.MsgTypeEnum;
import com.camel.chart.utils.FileUtil;
import com.camel.chart.utils.MsgUtil;
import com.camel.chart.utils.SerializeUtil;
import com.camel.chart.utils.entity.ApplicationConfig;
import com.camel.chart.utils.entity.ChartHistory;
import com.camel.chart.utils.entity.Msg;
import javafx.scene.control.Control;
import javafx.scene.control.ScrollPane;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.UUID;

public class TcpClient implements Runnable {


    private final int port;

    private Msg msg;

    private byte[] data;

    private final InetAddress ip;

    private final Control control;

    public TcpClient(int port, InetAddress ip, Msg msg, Control control) {
        this.port = port;
        this.ip = ip;
        this.msg = msg;
        this.control = control;
    }

    public TcpClient(int port, InetAddress ip, byte[] data, Control control) {
        this.port = port;
        this.ip = ip;
        this.data = data;
        this.control = control;
    }

    @Override
    public void run() {
        send();
    }

    public void send() {
        try {
            // 定义一个字符缓冲输入流
            if (ObjectUtil.isEmpty(msg)) {
                msg = Msg.deserialize(Arrays.copyOfRange(data, 0, 1000));
            }
            msg.setUuid(UUID.randomUUID().toString());
            // 把要访问的ip地址，端口号 和 数据放入
            msg.setDataTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            msg.setOriginAddr(ApplicationConfig.getInstance().getChartNetWorkInterface().getInetAddress());
            msg.setTargetAddr(ip.getHostAddress());
            // Platform.runLater 遵循FIFO  本地环境问题无法延时加载， 所以只有先更新本地UI
            switch (msg.getType()) {
                case TYPE_LOGIN:
                    System.err.println("请选择UDP");
                    break;
                case TYPE_LOGIN_CALLBACK:
                    System.err.println("请选择UDP");
                    break;
                case TYPE_TEXT_MSG:
                    MsgUtil.getInstance().addMsgUI((ScrollPane) control, msg);
                    sendAndSaveMsg();
                    break;
                case TYPE_FILE_REQ:
                    System.err.println("请选择UDP");
                    break;
                case TYPE_LOGOUT:
                    System.err.println("请选择UDP");
                    break;
                case TYPE_FILE_ACK:
                    System.err.println("请选择UDP");
                    break;
                case TYPE_FILE_MSG:
                    MsgUtil.getInstance().addMsgUI((ScrollPane) control, msg);
                    sendFileAndSaveFile();
                    break;
                default:
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }


    void sendMsg(Msg m) throws IOException {
        sendMsg(ObjectUtil.isEmpty(m) ? msg.serialze() : m.serialze(), null);
    }

    Socket sendMsg(byte[] bytes, Socket socket) throws IOException {
        OutputStream os = null;
        try {
            if (ObjectUtil.isEmpty(socket)) {
                socket = new Socket(ip, port);
            }
            os = socket.getOutputStream();
            os.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return socket;
    }

    void sendMsg() throws IOException {
        if (ObjectUtil.isEmpty(this.msg)) {
            if (!ObjectUtil.isEmpty(this.data)) {
                sendMsg(data, null);
            }
        } else {
            sendMsg(msg);
        }
    }

    void sendAndSaveMsg(Msg msg) throws IOException {
        sendMsg(msg);
        if (!ChartHistory.getInstance().getMsges().containsKey(ip.getHostAddress())) {
            ChartHistory.getInstance().getMsges().put(ip.getHostAddress(), new ArrayList<>());
        }
        ChartHistory.getInstance().getMsges().get(ip.getHostAddress()).add(msg);
        SerializeUtil.serializeObjAppendToFile(ip.getHostAddress() + ".txt", "history", msg);
    }

    void sendAndSaveMsg() throws IOException {
        sendMsg();
        if (!ChartHistory.getInstance().getMsges().containsKey(ip.getHostAddress())) {
            ChartHistory.getInstance().getMsges().put(ip.getHostAddress(), new ArrayList<>());
        }
        ChartHistory.getInstance().getMsges().get(ip.getHostAddress()).add(msg);
        SerializeUtil.serializeObjAppendToFile(ip.getHostAddress() + ".txt", "history", msg);
    }


    void sendFileAndSaveFile() throws IOException {
        FileUtil.FileEntity fileEntity = (FileUtil.FileEntity) this.msg.getMsg();

        byte[] msgByteArray = msg.serialze();

        File f = new File(fileEntity.getFilePath());
        if (!f.exists()) {
            throw new FileNotFoundException(fileEntity.getFilePath());
        }
        Socket socket = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(f));
            int buf_size = 4096;
            byte[] buffer = new byte[buf_size];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, buf_size))) {
//                bos.write(buffer, 0, len);
                socket = sendMsg(concatenate(msgByteArray, buffer), socket);
            }
            sendMsg(msgByteArray, socket);

            if (!ChartHistory.getInstance().getMsges().containsKey(ip.getHostAddress())) {
                ChartHistory.getInstance().getMsges().put(ip.getHostAddress(), new ArrayList<>());
            }

            ChartHistory.getInstance().getMsges().get(ip.getHostAddress()).add(msg);
            SerializeUtil.serializeObjAppendToFile(ip.getHostAddress() + ".txt", "history", msg);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.close();
        }
    }

    public static byte[] toByteArray(String filename) throws IOException {

        File f = new File(filename);
        if (!f.exists()) {
            throw new FileNotFoundException(filename);
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream((int) f.length());
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(f));
            int buf_size = 1024;
            byte[] buffer = new byte[buf_size];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, buf_size))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.close();
        }
    }

    public static byte[] concatenate(byte[]... arrays) {
        int length = 0;
        for (byte[] array : arrays) {
            length += array.length;
        }
        byte[] result = new byte[length];
        int pos = 0;
        for (byte[] array : arrays) {
            System.arraycopy(array, 0, result, pos, array.length);
            pos += array.length;
        }
        return result;
    }


}
